package kernel

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

var userRootPath string
var jetBrainsDataPath string
var pluginDirPath string
var pluginPath string
var agentString string

func init() {
	var err error
	defer func() {
		if err != nil {
			Exit()
		}
	}()
	switch runtime.GOOS {
	case "windows":
		jetBrainsDataPath = fmt.Sprintf("%cAppData%cRoaming%cJetBrains", os.PathSeparator, os.PathSeparator, os.PathSeparator)
	case "darwin":
		jetBrainsDataPath = fmt.Sprintf("%cLibrary%cApplication Support%cJetBrains", os.PathSeparator, os.PathSeparator, os.PathSeparator)
	default:
		err = fmt.Errorf("暂不支持的系统:%q", runtime.GOOS)
		fmt.Println(err)
		Exit()
	}
	//获取当前执行文件
	currentFile, err := exec.LookPath(os.Args[0])
	if err != nil {
		fmt.Println(fmt.Errorf("获取当前执行目录失败(%v)", err))
		return
	}
	//获取绝对路径
	path, err := filepath.Abs(currentFile)
	if err != nil {
		fmt.Println(fmt.Errorf("获取当前执行目录绝对路径失败(%v)", err))
		return
	}
	//获取目录地址
	pluginDirPath = filepath.Dir(path)
	pluginPath = fmt.Sprintf("%s%cja-netfilter.jar", pluginDirPath, os.PathSeparator)
	//判断插件是否存在
	_, err = os.OpenFile(pluginPath, os.O_RDONLY, os.FileMode(0666))
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			fmt.Println(fmt.Errorf("插件文件%q不存在", pluginPath))
			return
		}
		fmt.Println(fmt.Errorf("打开插件文件失败(%v)", err))
		return
	}
	agentString = fmt.Sprintf("-javaagent:%s", pluginPath)
	//获取当前用户
	u, err := user.Current()
	if err != nil {
		fmt.Println(fmt.Errorf("获取当前用户失败(%v)", err))
		return
	}
	//获取用户路径
	userRootPath = u.HomeDir
	//获取jetbrains 数据目录
	jetBrainsDataPath = fmt.Sprintf("%s%s", userRootPath, jetBrainsDataPath)
}

func macOsInit() (err error) {
	//获取当前执行文件
	currentFile, err := exec.LookPath(os.Args[0])
	if err != nil {
		fmt.Println(fmt.Errorf("获取当前执行目录失败(%v)", err))
		return
	}
	//获取绝对路径
	path, err := filepath.Abs(currentFile)
	if err != nil {
		fmt.Println(fmt.Errorf("获取当前执行目录绝对路径失败(%v)", err))
		return
	}
	//获取目录地址
	pluginDirPath = filepath.Dir(path)
	pluginPath = fmt.Sprintf("%s%cja-netfilter.jar", pluginDirPath, os.PathSeparator)
	//判断插件是否存在
	_, err = os.OpenFile(pluginPath, os.O_RDONLY, os.FileMode(0666))
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			fmt.Println(fmt.Errorf("插件文件%q不存在", pluginPath))
			return
		}
		fmt.Println(fmt.Errorf("打开插件文件失败(%v)", err))
		return
	}
	agentString = fmt.Sprintf("-javaagent:%s", pluginPath)
	//获取当前用户
	u, err := user.Current()
	if err != nil {
		fmt.Println(fmt.Errorf("获取当前用户失败(%v)", err))
		return
	}
	//获取用户路径
	userRootPath = u.HomeDir
	//获取jetbrains 数据目录
	jetBrainsDataPath = fmt.Sprintf("%s%s", userRootPath, jetBrainsDataPath)
	return nil
}

func Exit() {
	fmt.Printf("请按 Enter 结束...")
	fmt.Scanf("%s")
	os.Exit(0)
}

func GetAllJetbrainsApplication() ([]string, error) {
	//获取jetbrains所有产品
	fileList, err := ioutil.ReadDir(jetBrainsDataPath)
	if err != nil {
		fmt.Println(fmt.Errorf("扫描jetbrains产品失败(%v)", err))
		return nil, err
	}
	var ApplicationSlice []string
	fmt.Println("检测到产品如下:")
	for _, f := range fileList {
		if flag, _ := regexp.Match(`^[A-Za-z]+\d{4}.\d{1,2}$`, []byte(f.Name())); flag {
			fmt.Println(f.Name())
			ApplicationSlice = append(ApplicationSlice, f.Name())
		}
	}
	return ApplicationSlice, nil
}

func GetVmOptionsFile(Application string) (string, error) {
	fmt.Printf("\n\n开始获取应用:%q vmoptions配置文件\n", Application)
	applicationPath := fmt.Sprintf("%s%c%s%c", jetBrainsDataPath, os.PathSeparator, Application, os.PathSeparator)
	var vmFilePath string
	fileList, err := ioutil.ReadDir(applicationPath)
	if err != nil {
		fmt.Println(fmt.Errorf("读取应用%q下文件失败", Application))
		return "", err
	}
	for _, f := range fileList {
		if flag, _ := regexp.Match(`\w+.vmoptions`, []byte(f.Name())); flag {
			vmFilePath = fmt.Sprintf("%s%c%s%c%s", jetBrainsDataPath, os.PathSeparator, Application, os.PathSeparator, f.Name())
			break
		}
	}
	if vmFilePath == "" {
		fmt.Println(fmt.Errorf("获取应用%qvmoptions文件失败", Application))
		return "", err
	}
	return vmFilePath, nil
}

func WriteVmOptions(applicaton string, vmFilePath string) (err error) {
	vmFile, err := os.OpenFile(vmFilePath, os.O_RDWR, os.FileMode(0666))
	if err != nil {
		fmt.Println(fmt.Errorf("打卡应用%qvmoptions文件失败(%v)", applicaton, err))
		return
	}
	defer vmFile.Close()
	buff := bufio.NewReader(vmFile)
	var fileLines []string
	for {
		data, _, eof := buff.ReadLine()
		if eof == io.EOF {
			break
		}
		rgp := regexp.MustCompile("-javaagent:.*?ja-netfilter.jar$")
		arr := rgp.FindAllStringSubmatch(string(data), -1)
		if arr != nil {
			if arr[0] != nil && len(arr[0]) > 0 {
				continue
			}
		}
		fileLines = append(fileLines, string(data))
	}
	fileLines = append(fileLines, agentString)
	fileContent := strings.Join(fileLines, "\n")
	err = vmFile.Truncate(0)
	if err != nil {
		fmt.Println(fmt.Errorf("应用%q覆盖vmoptions文件失败", applicaton))
	}
	_, err = vmFile.WriteAt([]byte(fileContent), 0)
	err = vmFile.Truncate(0)
	if err != nil {
		fmt.Println(fmt.Errorf("应用%q覆盖vmoptions文件失败", applicaton))
	}
	_, err = vmFile.WriteAt([]byte(fileContent), 0)
	if err != nil {
		fmt.Println(fmt.Errorf("应用%q写入插件信息失败(%v)", applicaton, err))
		return
	}
	fmt.Printf("应用%q处理完毕\n", applicaton)
	return nil
}

func RemoveJavaagent(applicaton string, vmFilePath string) (err error) {
	vmFile, err := os.OpenFile(vmFilePath, os.O_RDWR, os.FileMode(0666))
	if err != nil {
		fmt.Println(fmt.Errorf("打卡应用%qvmoptions文件失败(%v)", applicaton, err))
		return
	}
	defer vmFile.Close()
	buff := bufio.NewReader(vmFile)
	var fileLines []string
	for {
		data, _, eof := buff.ReadLine()
		if eof == io.EOF {
			break
		}
		rgp := regexp.MustCompile("-javaagent:.*?ja-netfilter.jar$")
		arr := rgp.FindAllStringSubmatch(string(data), -1)
		if arr != nil {
			if arr[0] != nil && len(arr[0]) > 0 {
				continue
			}
		}
		fileLines = append(fileLines, string(data))
	}
	fileContent := strings.Join(fileLines, "\n")
	err = vmFile.Truncate(0)
	if err != nil {
		fmt.Println(fmt.Errorf("应用%q覆盖vmoptions文件失败", applicaton))
	}
	_, err = vmFile.WriteAt([]byte(fileContent), 0)
	if err != nil {
		fmt.Println(fmt.Errorf("应用%q写入插件信息失败(%v)", applicaton, err))
		return
	}
	fmt.Printf("应用%qJavaagent信息已清空\n", applicaton)
	return nil
}
